/* Compile with gcc -Wall -O3 -o as1_t2 as1_t2.c dummysort.c
   Then, start ./as1_t2
*/

#include "as1_t2.h"
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stddef.h>

long THREADS;

struct sortargs {
    task_t** tasks;
    task_t** temparray;
    int count;
    int threadcount;
};

void* sort(void* t) {
    struct sortargs args = *((struct sortargs*) t);
    if(args.count > 1) {
        int count1 = args.count >> 1;
        int count2 = args.count - count1;

        if(args.threadcount >= THREADS) {
            struct sortargs newargs[2] = {{args.tasks, args.temparray, count1, args.threadcount},
                {args.tasks + count1, args.temparray + count1, count2, args.threadcount}
            };
            sort( (void*) &newargs[0]);
            sort( (void*) &newargs[1]);
        }
        else {
            pthread_t thread[2];
            int rc, i;
            struct sortargs newargs[2] = {{args.tasks, args.temparray, count1, args.threadcount * 2},
                {args.tasks + count1, args.temparray + count1, count2, args.threadcount * 2}
            };
            for(i = 0; i < 2; i++) {
                rc = pthread_create(&thread[i], NULL, &sort, (void*) &newargs[i]);
                if (rc) {
                    printf("ERROR: return code from pthread_create() is %d\n", rc);
                    exit(-1);
                }
            }
            for(i = 0; i < 2; i++) {
                rc = pthread_join(thread[i], NULL);
                if (rc) {
                    printf("ERROR: return code from pthread_join() is %d\n", rc);
                    exit(-1);
                }
            }
        }
        register int i = 0, j = 0, k = 0;
        while(i < count1 && j < count2) {
            if(args.tasks[i]->id < args.tasks[count1 + j]->id) {
                args.temparray[k++] = args.tasks[i++];
            }
            else {
                args.temparray[k++] = args.tasks[count1 + j++];
            }
        }
	if(i < count1)	  memcpy( args.temparray + k, args.tasks + i,sizeof(task_t*) * (count1-i));
	else if(j < count2)	  memcpy(args.temparray + k, args.tasks + count1 + j,sizeof(task_t*) * (count2-j));
	memcpy( args.tasks, args.temparray,sizeof(task_t*) * (args.count));
    }
    if(args.threadcount >= THREADS >> 1) {
        return NULL;
    }
    else {
        pthread_exit(NULL);
        return NULL;
    }
}

void msort(task_t** tasks, int count) {
    THREADS = sysconf( _SC_NPROCESSORS_ONLN );
    task_t **temparray = (task_t**) malloc(sizeof(task_t*) * count);
    struct sortargs args = {tasks, temparray, count, 1};
    pthread_t thread;
    pthread_create(&thread, NULL, &sort, (void*) &args);
    pthread_join(thread, NULL);
    free(temparray);
}
